package com.subl.mybatis.config;

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.subl.mybatis.aop.GlobalTransactionAspect;
import com.subl.mybatis.xa.IssXADataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.*;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.util.Assert;

import javax.sql.DataSource;


@Configuration
@EnableScheduling
@ConditionalOnClass(MybatisProperties.class)
@EnableConfigurationProperties({MybatisProperties.class})
//@ComponentScan("com.subl.mybatis")
public class DataSourceAutoConfiguration{

    private final MybatisProperties properties;

    public DataSourceAutoConfiguration(MybatisProperties properties) {
        this.properties = properties;
    }

    /**
     * sqlSessionFactory全局配置
     *
     * @return GlobalConfig
     */
    @Bean
    @ConditionalOnMissingBean(GlobalConfig.class)
    public GlobalConfig globalConfig() {
        GlobalConfig globalConfig = new GlobalConfig();
        return globalConfig;
    }


    @Primary
    @Bean(name = "DataSource#ds0")
    @ConditionalOnMissingBean(DataSource.class)
    public DataSource dataSource() {
        HikariDataSource dataSource = properties.getDataSourceMap().get("ds0");
        Assert.notNull(dataSource, "至少配置一个数据源ds0");
        return dataSource;
    }

    @Bean(name = "DataSource#ds1")
    @ConditionalOnProperty(prefix = "spring.datasource.hikari.data-source-map.ds1", name = "jdbc-url")
    public DataSource dataSource1() {
        return properties.getDataSourceMap().get("ds1");
    }

    /**
     * 分页拦截器插件
     *
     * @return PaginationInterceptor
     */
    @Bean
    @ConditionalOnMissingBean(PaginationInterceptor.class)
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作， true调回到首页，false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量，默认 500 条，-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }

    @Primary
    @Bean("SqlSessionFactory#ds0")
    @ConditionalOnBean(name = "DataSource#ds0")
    public SqlSessionFactory sqlSessionFactory(GlobalConfig globalConfig) throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(dataSource());
        //设置mapper路径
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactory.setMapperLocations(
                resolver.getResources(this.properties.getMapperLocations()));
        //mybatis设置
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        sqlSessionFactory.setConfiguration(configuration);
        //添加各种拦截器插件
        sqlSessionFactory.setPlugins(paginationInterceptor());
        sqlSessionFactory.setGlobalConfig(globalConfig);
        return sqlSessionFactory.getObject();
    }

    @Bean("SqlSessionFactory#ds1")
    @ConditionalOnBean(name = "DataSource#ds1")
    public SqlSessionFactory sqlSessionFactory1(GlobalConfig globalConfig) throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(dataSource1());
        //设置mapper路径
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactory.setMapperLocations(
                resolver.getResources(this.properties.getMapperLocations()));
        //mybatis设置
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        sqlSessionFactory.setConfiguration(configuration);
        //PerformanceInterceptor(),OptimisticLockerInterceptor()
        //添加各种拦截器插件
        sqlSessionFactory.setPlugins(paginationInterceptor());
        sqlSessionFactory.setGlobalConfig(globalConfig);
        return sqlSessionFactory.getObject();
    }

    @Bean("IssXADataSource#ds0")
    @ConditionalOnBean(name = "DataSource#ds0")
    public IssXADataSource issXADataSource() {
        return new IssXADataSource(dataSource());
    }

    @Bean("IssXADataSource#ds1")
    @ConditionalOnBean(name = "DataSource#ds1")
    public IssXADataSource issXADataSource1() {
        return new IssXADataSource(dataSource1());
    }

    /**
     * XA超时事务、异常事务监控组件
     * @return
     */
    @Bean
    @ConditionalOnBean(name = "DataSource#ds1")
    public XAMonitor xaMonitor() {
        return new XAMonitor();
    }

    /**
     * @GlobalTransaction注解的切面
     * @return
     */
    @Bean
    public GlobalTransactionAspect globalTransactionAspect() {
        return new GlobalTransactionAspect();
    }

}
